# 决策树

"""
决策树是广泛用于分类和回归任务的模型。本质上，它从一层层的 if/else 问题中进行学习，并得出结论。

想象一下，你想要区分下面这四种动物：熊、鹰、企鹅和海豚。
你的目标是通过提出尽可能少的 if/else 问题来得到正确答案。
可以问三个问题：

这种动物有没有羽毛？
如果有羽毛，是鹰或企鹅。
如果没有羽毛，是熊或海豚。

有羽毛的动物会不会飞？
如果会飞，是鹰。
如果不会飞，是企鹅。

没有羽毛的动物有没有鳍？
如果有鳍，是海豚。
如果没有鳍，是熊
"""
import mglearn.plots
import numpy as np
from IPython.core.display_functions import display
from matplotlib import pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split


'''
树的每个结点代表一个问题或一个包含答案的终结点（也叫叶结点）。
树的边将问题的答案与将问的下一个问题连接起来。
用机器学习的语言来说就是，为了区分四类动物（鹰、企鹅、海豚和熊），
我们利用三个特征（“有没有羽毛”“会不会飞”和“有没有鳍”）来构建一个模型。
我们可以利用监督学习从数据中学习模型，而无需人为构建模型。
'''

# 构造决策树
'''
学习决策树就是学习一系列条件判断，以便快速得出正确答案。在机器学习中，这些条件判断称为测试，
不同于用于评估模型的测试集。数据通常不是简单的二元特征，而是连续的。

对于连续特征，测试通常是：“特征i的值是否大于a？”通过递归地进行这样的测试，可以构建一个二元决策树，
每个节点都包含一个测试，相当于沿一个轴划分数据。这种方法将算法视为分层划分。
由于每个测试只关注一个特征，所以划分后的区域边界总是与坐标轴平行。
这个过程会一直进行，直到每个区域（叶节点）只包含单一目标值（单一类别或回归值）。
如果叶节点包含的数据点目标值都相同，那么这个叶节点就是纯净的。

预测新数据点时，需要确定它属于哪个划分区域，然后使用该区域的多数目标值（如果是纯净叶节点，就是单一目标值）作为预测结果。
从根节点开始遍历树，根据测试结果决定向左或向右移动，直到找到对应的区域。
决策树也适用于回归任务，预测方法是找到新数据点所属的叶节点，然后输出该叶节点中所有训练点的平均目标值。
'''

'''
构造决策树直到所有叶节点都是纯净的，会导致模型复杂且过度拟合训练数据。这意味着树在训练集上的准确率是100%，
每个数据点都被正确分类。但这样的树可能过于关注训练数据中的异常点，导致决策边界不自然。

为了避免过拟合，有两种主要策略：
预剪枝（pre-pruning）：在树完全生长前停止，通过限制树的最大深度、叶节点的最大数量或每个节点的最小数据点数来实现。
后剪枝（post-pruning）或剪枝（pruning）：先让树完全生长，然后移除或合并信息量小的节点。

scikit-learn中的决策树实现在DecisionTreeRegressor和DecisionTreeClassifier类中。scikit-learn只支持预剪枝，不支持后剪枝。
'''

# 我们在乳腺癌数据集上更详细地看一下预剪枝的效果。和前面一样，我们导入数据集并将
# 其分为训练集和测试集。然后利用默认设置来构建模型，默认将树完全展开（树不断分
# 支，直到所有叶结点都是纯的）。我们固定树的 random_state，用于在内部解决平局问题：
from sklearn.tree import DecisionTreeClassifier
cancer = load_breast_cancer()
X_train,X_test,y_train,y_test = train_test_split(cancer.data,cancer.target,stratify=cancer.target,random_state=42)
tree=DecisionTreeClassifier(random_state=0)
tree.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))
# Accuracy on training set: 1.000
# Accuracy on test set: 0.937

'''
确实，如果决策树的叶节点都是纯净的，那么它在训练集上的精度会非常高，因为它能够完美地记住训练数据的所有标签。
但是，这种树的深度会非常大，导致模型复杂度过高，容易过拟合。
过拟合的模型在训练集上表现很好，但在测试集上的表现会下降，因为它无法很好地泛化到新数据。

在您提到的情况下，测试集上的精度比线性模型略低，线性模型的精度约为95%。
如果不对决策树的深度进行限制，它的深度和复杂度可能会变得非常大，导致模型容易过拟合，对新数据的泛化能力差。

通过预剪枝，我们可以在树完全生长之前停止它的展开，以减少过拟合。例如，设置max_depth=4意味着树在连续问了
4个问题后就会停止生长。这种限制可以减少过拟合，虽然可能会降低训练集的精度，但有助于提高测试集的精度，因为模型变得更加简洁，
能够更好地泛化到未见过的数据。这种方法有助于平衡模型的偏差和方差，从而在训练集和测试集之间取得更好的性能。
'''

tree = DecisionTreeClassifier(max_depth=4,random_state=0)
tree.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(tree.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(tree.score(X_test, y_test)))
# Accuracy on training set: 0.988
# Accuracy on test set: 0.951

'''
分析决策树
要分析决策树，我们可以使用tree模块的export_graphviz函数来可视化决策树。
这个函数会生成一个.dot格式的文件，这是一种用于保存图形的文本文件格式。
通过设置合适的参数，我们可以让生成的树图更加直观和易于理解。

以下是如何使用export_graphviz函数的一些关键步骤：
 设置节点颜色：通过设置filled=True参数，可以让每个节点根据其多数类别显示不同的颜色。这有助于快速识别每个节点的主要类别。
 传入类别名称：通过class_names参数，可以指定类别名称。这样，在生成的树图中，每个叶节点会显示对应的类别名称，而不是默认的类别索引。
 传入特征名称：通过feature_names参数，可以指定特征名称。这样，在树图中，每个测试节点会显示对应的特征名称，而不是特征的索引，使得树图更容易理解。
'''

from sklearn.tree import export_graphviz
# tree: 这是已经训练好的决策树模型对象。
# out_file='tree.dot': 指定输出文件的名称和路径。
# class_names=["malignant","benign"]: 这个参数指定了类别名称的列表。
# feature_names=cancer.feature_names: 这个参数指定了特征名称的列表。
# impurity=False: 这个参数指示是否在节点中显示不纯度度量（如基尼不纯度）
# filled=True: 这个参数指示是否根据节点的类别填充颜色。
export_graphviz(tree, out_file='../tree.dot', class_names=["malignant", "benign"], feature_names=cancer.feature_names, impurity=False, filled=True)

# 利用 graphviz 模块读取这个文件并将其可视化
import graphviz
with open("../tree.dot") as f:
 dot_graph = f.read()
graphviz.Source(dot_graph)

'''
树的可视化有助于理解决策树的预测过程，并且易于向非专业人士解释。然而，即使树的深度只有4层，也可能显得复杂。
对于深度更大的树（如10层），理解起来更加困难。一种有效的方法是关注大部分数据的实际路径，这可以帮助我们更好地把握模型的核心逻辑.
'''

'''
特征重要性（feature importance）是决策树的一个重要属性，它帮助我们理解每个特征在模型中的作用。
特征重要性是一个介于0和1之间的数值，0表示该特征在模型中没有被用到，而1表示该特征完美预测了目标值。
所有特征重要性的总和为1，这意味着它们共同解释了模型的预测能力。
通过查看特征重要性，我们可以快速识别出对模型预测最有贡献的特征，从而更好地理解模型的工作原理，而不必查看整个树结构.
'''

print("Feature importances:\n{}".format(tree.feature_importances_))

# 我们可以将特征重要性可视化，与我们将线性模型的系数可视化的方法类似
def plot_feature_importances_cancer(model):
 n_features = cancer.data.shape[1]
 plt.barh(range(n_features), model.feature_importances_,align="center")
 plt.yticks(np.arange(n_features),cancer.feature_names)
 plt.xlabel("Feature importance")
 plt.ylabel("Feature")
 plt.show()
# plot_feature_importances_cancer(tree)

'''
在决策树中，顶部划分使用的特征（如“worst radius”）具有最高的重要性，这表明它在区分不同类别中起到了关键作用。
这与我们通过分析树结构得出的结论一致，即第一层划分有效地将两个类别分开。

然而，一个特征的feature_importance_值很小，并不意味着该特征没有提供任何信息。
这可能意味着该特征没有被树模型选中，原因可能是另一个特征已经包含了相同的信息，或者模型找到了更好的特征来区分类别。

与线性模型中的系数不同，决策树的特征重要性始终是正数，它不能指示特征与特定类别的直接关系。
特征重要性告诉我们“worst radius”（最大半径）很重要，但并不直接告诉我们半径较大时样本是良性还是恶性。
实际上，特征与类别之间的关系可能并不简单，需要进一步分析才能确定。
'''

tree = mglearn.plots.plot_tree_not_monotone()
display(tree)

'''
决策树不仅可以用于分类，还可以用于回归任务，其在DecisionTreeRegressor中实现。
回归树的使用和分析与分类树相似，但在使用基于树的模型进行回归时，
有一个重要特性需要指出：DecisionTreeRegressor（以及所有基于树的回归模型）不能进行外推，
也就是说，它们无法在训练数据范围之外进行有效预测。
'''

'''
决策树的优点、缺点和参数如下：

优点：
 易于理解：决策树模型容易可视化，非专家也能理解模型的决策过程，尤其是对于较小的树。
 不受数据缩放影响：决策树算法不需要特征预处理，如归一化或标准化，因为每个特征都是单独处理的，数据的划分不依赖于特征的尺度。
 适用于不同类型特征：决策树可以很好地处理特征尺度完全不同的情况，包括二元特征和连续特征。
缺点：
 过拟合：即使使用了预剪枝技术，决策树仍然容易过拟合，导致模型的泛化能力差。
 稳定性问题：决策树对训练数据中的小变化非常敏感，这可能导致不同的训练集产生完全不同的树结构。
参数：
 max_depth：树的最大深度。限制树的深度可以防止树变得过于复杂。
 max_leaf_nodes：叶节点的最大数量。限制叶节点的数量可以控制树的生长。
 min_samples_leaf：叶节点的最小样本数。这个参数确保叶节点中至少有一定数量的样本，避免过拟合。
在实际应用中，由于单棵决策树容易过拟合，通常会使用集成方法（如随机森林、梯度提升树等）来提高模型的泛化能力和稳定性。
这些集成方法通过组合多棵决策树来减少过拟合，提高模型的预测性能。
'''

# 决策树集成
'''
集成学习是通过合并多个机器学习模型来构建更强大模型的方法。
在机器学习中，随机森林和梯度提升决策树是两种基于决策树的集成模型，被证明对大量分类和回归数据集有效。

随机森林
随机森林通过构建多棵决策树来解决单棵决策树容易过拟合的问题。
每棵树的预测可能相对较好，但对部分数据过拟合。通过取多棵树预测结果的平均值，可以降低过拟合，同时保持预测能力。

构造过程
 1.树的数量：确定随机森林中树的数量（n_estimators参数）。例如，构造10棵树。
 2.自助采样：对每棵树进行自助采样（bootstrap sampling），即从数据集中有放回地随机抽取样本，
  创建与原数据集大小相同的新数据集。这样，每棵树使用的数据集略有不同。
 3.特征选择：在每个节点处，随机选择特征的一个子集进行划分，而不是考虑所有特征。
  选择的特征个数由max_features参数控制。每个节点的特征选择是独立的，使得每棵树的划分不同。
关键参数
 max_features：控制每个节点划分时考虑的特征数量。如果设置为所有特征，则没有特征选择的随机性；
  如果设置为1，则每次划分只能对一个随机选择的特征进行测试。
预测过程
 回归：对每棵树的预测结果取平均值。
 分类：使用“软投票”策略，即对每棵树的预测概率取平均值，然后选择概率最大的类别作为最终预测结果。
随机森林通过引入随机性来增加树之间的差异性，从而提高模型的泛化能力和稳定性。

'''

# 分析随机森林。下面将由 5 棵树组成的随机森林应用到前面研究过的 two_moons 数据集上：
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons
X,y = make_moons(n_samples=100, noise=0.25, random_state=3)
X_train,X_test,y_train,y_test = train_test_split(X,y,stratify=y,random_state=42)
forest = RandomForestClassifier(n_estimators=5, random_state=2)
forest.fit(X_train,y_train)

# 作为随机森林的一部分，树被保存在 estimator_ 属性中。我们将每棵树学到的决策边界可视化，
# 也将它们的总预测（即整个森林做出的预测）可视化

fig, axes = plt.subplots(2,3,figsize=(20,10))
# axes 是一个包含多个轴对象的数组，axes.ravel() 将 axes 数组转换为一维数组。ravel 方法返回一个视图，其中数组被展平为一维，这使得可以方便地迭代每个轴对象
# forest.estimators_ estimators_ 属性包含了模型中的所有基学习器（在这个例子中是决策树）。每个基学习器都是一个单独的模型，用于进行预测

for i,(ax,tree) in enumerate(zip(axes.ravel(),forest.estimators_)):
 ax.set_title(f"Tree {i}")
 mglearn.plots.plot_tree_partition(X_train,y_train,tree,ax=ax)
mglearn.plots.plot_2d_separator(forest,X_train,fill=True,ax=axes[-1,-1],alpha=0.4)
axes[-1,-1].set_title("Random Forest")
mglearn.discrete_scatter(X_train[:,0],X_train[:,1],y_train)
# plt.show()
# 生成5棵随机化的决策树找到的决策边界，以及将它们的预测概率取平均后得到的决策边界

'''
随机森林通过集成多棵树的预测来减少过拟合，提高模型的泛化能力。以下是随机森林的一些关键优势和应用实例：

随机森林的优势
 多样性：每棵树由于自助采样和特征选择的随机性，学到的决策边界不同，这增加了模型的多样性，有助于减少过拟合。
 泛化能力：随机森林比单棵决策树具有更好的泛化能力，因为它综合了多棵树的预测结果，减少了对训练数据的过度拟合。
 直觉性：随机森林的决策边界更加符合直觉，因为它是通过多棵树的边界平均得到的，更平滑，更能捕捉数据的真实分布。
 实用性：在实际应用中，通常使用几百或上千棵树来构建随机森林，以获得更平滑、更准确的决策边界。
'''

X_train, X_test, y_train, y_test = train_test_split(cancer.data,cancer.target,random_state=0)
forest = RandomForestClassifier(n_estimators=100, random_state=0)
forest.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(forest.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(forest.score(X_test, y_test)))

# Accuracy on training set: 1.000
# Accuracy on test set: 0.972

'''
在没有调节任何参数的情况下，随机森林的精度为 97%，比线性模型或单棵决策树都要好。
我们可以调节 max_features 参数，或者像单棵决策树那样进行预剪枝。但是，随机森林的默认参数通常就已经可以给出很好的结果。
'''

'''
与决策树类似，随机森林也可以给出特征重要性，计算方法是将森林中所有树的特征重要性求和并取平均。
一般来说，随机森林给出的特征重要性要比单棵树给出的更为可靠。
'''

plot_feature_importances_cancer(forest)

'''
确实，随机森林与单棵决策树相比，在特征重要性方面表现出一些不同的特点：

 1.更多非零特征重要性：在随机森林中，由于集成了多棵树的预测，更多的特征可能会显示出重要性不为零。
  这是因为每棵树可能基于不同的随机样本和特征子集进行训练，从而捕捉到不同特征对预测的贡献。
 2.特征重要性的综合评估：随机森林对特征重要性的评估是多棵树评估结果的综合，这使得随机森林能够更全面地把握数据的特征。
  即使某个特征在单棵树中未被选中，它也可能在其他树中很重要，因此在随机森林中仍然具有一定的重要性。
 3.“worst radius”和“worst perimeter”的重要性：在你提到的例子中，尽管“worst radius”（最大半径）在单棵决策树中
  可能被赋予很高的特征重要性，随机森林可能会发现“worst perimeter”（最大周长）总体上具有更大的信息量。这表明随机森林能够通过集成多棵树的视角，识别出对预测结果影响最大的特征。

随机性带来的全面性：随机森林中的随机性迫使算法探索多种可能的特征组合和解释，这有助于模型从整体上理解数据的特征，而不是仅仅依赖于单棵树的视角。
总的来说，随机森林通过集成多棵树的预测和特征重要性评估，能够提供更全面和稳健的特征重要性分析，这有助于我们更好地理解模型的决策过程和数据的特征。

'''

'''
随机森林是一种强大的集成学习方法，广泛应用于分类和回归任务。以下是其优点、缺点和关键参数的总结：

优点
 强大且鲁棒：通常不需要复杂的参数调整就能获得良好的结果。
 无需数据缩放：不需要对数据进行归一化或标准化处理。
 处理多种数据类型：能够很好地处理不同尺度的特征和混合类型的数据。
 特征重要性评估：提供特征重要性评分，有助于理解特征对模型预测的贡献。
 并行计算友好：可以利用多核处理器进行并行计算，提高训练速度。
缺点
 模型复杂性：模型由多棵树组成，难以进行详细的解释和可视化。
 训练时间长：在大型数据集上训练可能需要较长时间。
 内存需求高：需要较大的内存来存储模型和数据。
 对稀疏数据表现不佳：在处理维度非常高的稀疏数据（如文本数据）时，可能不如线性模型表现好。
关键参数
 n_estimators：树的数量。增加树的数量可以降低过拟合，提高模型的鲁棒性，但会增加内存需求和训练时间。
 max_features：每个节点划分时考虑的特征数量。较小的值可以增加树之间的差异性，降低过拟合。对于分类，默认值是sqrt(n_features)；对于回归，默认值是n_features。
 max_depth：树的最大深度。限制树的深度可以防止过拟合，但过小的值可能导致欠拟合。
 min_samples_split：分裂内部节点所需的最小样本数。
 min_samples_leaf：叶节点所需的最小样本数。
 random_state：随机状态。固定该参数可以确保模型的可重复性。
使用建议
 并行计算：通过设置n_jobs参数来利用多核处理器加速训练过程。
 参数调整：在时间和内存允许的情况下，尽量增加n_estimators的值。对于max_features，通常使用默认值即可，但可以根据具体情况进行调整。
 数据类型选择：对于稀疏数据或对时间和内存要求较高的应用，可以考虑使用线性模型作为替代。
'''

# 梯度提升回归树（梯度提升机）
'''
梯度提升回归树（Gradient Boosting Regression Trees, GBRT）是一种强大的集成学习方法，
它通过逐步添加决策树来优化模型性能。以下是对GBRT的进一步解释和关键点总结：

工作原理
 连续改进：GBRT通过顺序添加决策树，每棵树都尝试修正前一棵树的预测误差，从而逐步提升模型性能。
 弱学习器：使用的是简单的模型（弱学习器），通常是深度较浅的决策树，因为它们训练更快，且有助于防止过拟合。
 无随机化：与随机森林不同，GBRT不引入随机性，而是通过逐步优化来构建模型。
优点
 灵活性：适用于分类和回归问题。
 高性能：在许多机器学习竞赛中表现优异，被广泛应用于工业界。
 内存和速度：使用深度较小的树减少了内存占用，提高了预测速度。
缺点
 参数敏感：对参数设置较为敏感，需要仔细调整以获得最佳性能。
 训练时间：尤其是在大型数据集上，训练时间可能较长。
关键参数
 n_estimators：树的数量，增加树的数量可以提高模型性能，但也可能增加过拟合的风险。
 learning_rate：学习率，控制每棵树对前一棵树错误的修正强度。较高的学习率意味着每棵树的影响更大，可能导致模型复杂度过高。
 max_depth：树的最大深度，限制树的深度可以防止过拟合，同时减少模型的复杂度和内存占用。
 min_samples_split：分裂内部节点所需的最小样本数。
 min_samples_leaf：叶节点所需的最小样本数。
参数调整建议
 学习率和树的数量：较低的学习率可能需要更多的树（较大的n_estimators），而较高的学习率可能需要较少的树。这两者需要相互配合调整。
 预剪枝：通过设置max_depth、min_samples_split和min_samples_leaf来控制树的复杂度，防止过拟合。
GBRT通过精心设计的迭代过程和参数调整，可以构建出非常强大的模型。正确设置参数后，
GBRT通常能够提供比随机森林更高的精度。然而，这也意味着需要更多的调参工作和对模型的理解。
'''

# 下面是在乳腺癌数据集上应用 GradientBoostingClassifier 的示例。默认使用 100 棵树，最大深度是 3，学习率为 0.1：

from sklearn.ensemble import GradientBoostingClassifier
X_train, X_test, y_train, y_test = train_test_split(cancer.data,cancer.target,random_state=0)
gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on training set: 1.000
# Accuracy on test set: 0.965

# 由于训练集精度达到 100%，所以很可能存在过拟合。为了降低过拟合，我们可以限制最
# 大深度来加强预剪枝，也可以降低学习率：
gbrt = GradientBoostingClassifier(random_state=0,max_depth=1)
gbrt.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on training set: 0.991
# Accuracy on test set: 0.972

gbrt = GradientBoostingClassifier(random_state=0,learning_rate=0.01)
gbrt.fit(X_train,y_train)
print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on training set: 0.988
# Accuracy on test set: 0.958

'''
降低模型复杂度的两种方法都降低了训练集精度，这和预期相同。在这个例子中，减小树的最大深度显著提升了模型性能，而降低学习率仅稍稍提高了泛化性能。
'''

'''
可以看到，梯度提升树的特征重要性与随机森林的特征重要性有些类似，不过梯度提升完全忽略了某些特征。
由于梯度提升和随机森林两种方法在类似的数据上表现得都很好，因此一种常用的方法就是先尝试随机森林，它的鲁棒性很好。
如果随机森林效果很好，但预测时间太长，或者机器学习模型精度小数点后第二位的提高也很重要，那么切换成梯度提升通常会有用。
如果你想要将梯度提升应用在大规模问题上，可以研究一下 xgboost 包及其 Python 接口，
在写作本书时，这个库在许多数据集上的速度都比 scikit-learn 对梯度提升的实现要快
（有时调参也更简单）。
'''

'''
梯度提升决策树（Gradient Boosting Decision Trees, GBDT）以下是GBDT的优点、缺点和主要参数的总结：

优点
 强大的预测性能：GBDT通常在各种数据集上都能提供出色的预测结果。
 易于处理各种数据类型：不需要对数据进行缩放，适用于包含二元特征和连续特征的数据集。
 特征重要性评估：能够提供特征重要性的评估，帮助理解模型决策。
 处理缺失值：能够处理数据中的缺失值。
缺点
 参数调优复杂：需要仔细调整参数以获得最佳性能。
 训练时间长：尤其是在大型数据集上，训练过程可能耗时较长。
 过拟合风险：如果参数设置不当，模型可能会过拟合。
 不适用于高维稀疏数据：对于高维稀疏数据（如文本数据），GBDT可能不如其他模型表现好。
主要参数
 n_estimators：树的数量。这个参数决定了模型的复杂度和训练时间。增加树的数量可以提高模型的性能，但也可能增加过拟合的风险。
 learning_rate：学习率。控制每棵树对前一棵树错误的修正强度。较低的学习率可能需要更多的树来达到相似的模型复杂度。
 max_depth：树的最大深度。这个参数用于控制每棵树的复杂度，防止过拟合。通常设置得较小，以保持树的简单性。
 min_samples_split：分裂内部节点所需的最小样本数。
 min_samples_leaf：叶节点所需的最小样本数。
 max_features：在寻找最佳分割时要考虑的特征数量，可以增加模型的多样性。
参数调整建议
 n_estimators和learning_rate的平衡：通常需要在这两个参数之间找到平衡。较低的学习率可能需要更多的树，而较高的学习率可能需要较少的树。
 max_depth的设置：为了保持模型的简单性和防止过拟合，通常将max_depth设置得较小。
 交叉验证：使用交叉验证来选择最佳的参数组合，尤其是在参数空间较大时。
 
GBDT因其出色的性能而受到广泛使用，但也需要更多的关注和调整来避免过拟合和优化模型性能。
对于大规模问题，可以考虑使用XGBoost等优化的梯度提升库，它们在速度和易用性上通常优于标准的GBDT实现。
'''
